<div class="container w1100 clearfloat">
  <div class="column mainCol l">
    <div id="content1" class="content">
      <h2 class="title">栈与堆</h2>
      <p class="detialInfo">与c/c++不同，js中并没有严格意义上区分栈内存与堆内存。可以粗浅的理解为js的所有数据都保存在堆内存中。可是在某些场景，仍然需要基于堆栈数据结构的思路进行处理，比如js的执行上下文，执行上下文在逻辑上实现了堆栈。兵乓球的存放方式与栈中存取数据的方式如出一辙。处于盒子中最顶层的兵乓球，它一定是最后被放进去，但可以最先被使用。而想要使用底层的兵乓球，就必须奖上面的兵乓球取出来，让底层的兵乓球处于盒子顶层。这就是栈空间是先进后出，后进先出的特点。</p>
      <p class="detialInfo">堆存取数据的方式则和书架与书非常相似。书虽然整齐的存放在书架上，但只要知道书的名字，就可以很方便的取出我们想要的书，而不用像从兵乓球盒子里取兵乓球一样，非得要将上面的所有兵乓球拿出来才能取到中间的某一个兵乓球。好比在json格式的数据中，我们存储的key-value是可以无序的，因为顺序的不同并不影响我们的使用，我们只需要关心书的名字。</p>
    </div>
    <div id="content2" class="content">
      <h2 class="title">变量对象与基础数据类型</h2>
      <p class="detialInfo">js的执行上下文生成之后，会创建一个叫做变量对象的特殊对象，js的基础数据类型往往都会保存在变量对象中。严格意义来说，变量对象也是存放于堆内存中。但是由于变量对象的特殊职能，我们在理解时，仍然需要将其于堆内存区分开来。</p>
      <p class="detialInfo">基础数据类型都是一些简单的数据段。js中有undefined、null、boolean、number、string这5种基础数据类型。基础数据类型都是按值访问，可以直接操作保存在变量中的实际值。</p>
    </div>
    <div id="content3" class="content">
      <h2 class="title">引用数据类型与堆内存</h2>
      <p class="detialInfo">与其他语言不通，js的引用数据类型，比如数组array，它值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。js不允许直接访问堆内存中的位置。因此不能直接操作对象的堆内存空间。在操作对象时，实际上是在操作对象的引用而不是实际的对象。因此，引用类型的值都是按引用访问的。</p>
      <p class="detialInfo">为了更好的搞懂变量对象与堆内存，可以结合一下例子进行理解：</p>
      <pre><code class="javascript">var al = 0;/*变量对象*/<br>var a2 = 'this is string';/*变量对象*/<br>var a3 = null;/*变量对象*/<br>var b = {m:20};/*变量b存在于变量对象中，{m:20}作为对象存在于堆内存中*/<br>var c = [1, 2, 3];/*变量c存在于变量对象中，[1, 2, 3]作为对象存在于堆内存中*/</code></pre>
      <p class="detialInfo">要访问堆内存中的引用数据类型时，实际上首先是从变量对象中获取了该对象的地址引用，然后再从堆中取得我们需要的数据。</p>
      <pre><code class="javascript">var a = 20,b = a;<br>b = 30;console.log(a);/*20*/<br><br>var m = {a:10,b:20};<br>var n = m; n.a = 15;<br>console.log(m.a);/*15*/</code></pre>
      <p class="detialInfo">在变量对象中的数据发生复制行为时，系统会自动为新的变量分配一个新值。var b = a;执行之后，a与b虽然值都相于20，但是他们已经是相互独立互不影响的值了。所以我们修改了b的值以后，a的值并不会发生变化</p>
      <p class="detialInfo">在第二个例子中，通过var n = m执行一次复制引用类型的操作。引用类型的复制同样会为新的变量自动分配一个新的值保存在变量对象中。但不同的是，这个新的值，仅仅只是引用类型的一个地址指针，当地址指针相同时，尽管他们相互独立，但是在变量对象中访问到的具体对象实际上是同一个。因此当我改变n时，m也发生了变化。这就是引用类型的特性。</p>
    </div>
    <div id="content4" class="content">
      <h2 class="title">内存空间管理</h2>
      <p class="detialInfo">js的内存生命周期：</p>
      <ul class="infoList">
        <li>分配你所需要的内存。</li>
        <li>使用分配到的内存（读、写）</li>
        <li>不需要时将其释放、归还</li>
      </ul>
      <p class="detialInfo">便于理解，使用一个简单的例子来解释这个周期：</p>
      <pre><code class="javascript">var a = 20;/*在内存中给数值变量分配空间*/<br>alert(a = 100);/*使用内存*/<br>a = null;/*使用完成之后，释放内存空间*/<br></code></pre>
      <p class="detialInfo">要访问堆内存中的引用数据类型时，实际上首先是从变量对象中获取了该对象的地址引用，然后再从堆中取得我们需要的数据。</p>
      <pre><code class="javascript">var a = 20,b = a;<br>b = 30;console.log(a);/*20*/<br><br>var m = {a:10,b:20};<br>var n = m; n.a = 15;<br>console.log(m.a);/*15*/</code></pre>
      <p class="detialInfo">js有自动垃圾收集机制，就是找出那些不再继续使用的值，然后释放期占用的内存。垃圾收集器会每隔固定的时间段就执行一次释放操作。</p>
      <p class="detialInfo">在js中最常用的是通过标记清除的算法来找到哪些对象不再继续使用的。因此a = null其实仅仅只是做了一个释放引用的操作，让a原本对象的值失去引用，脱离执行环境，这个值会在下一次垃圾收集执行操作时被找到并释放。在适当的时候解除引用，是为页面获得更好性能的一个重要方式。</p>
      <p class="detialInfo">在局部作用域中，当函数执行完毕，局部变量也就没有存在的必要了。因此垃圾收集器很容易做出判断并回收。但是全局变量是什么时候需要自动释放内存空间则很难判断，因为在开发中，需要尽量避免使用全局变量，以确保性能问题。</p>
    </div>
    <div id="content5" class="content">
      <h2 class="title">执行上下文</h2>
      <pre><code class="javascript">console.log(a);/*undefined*/<br>var a = 20;</code></pre>
      <p class="detialInfo">每次当控制器转到可执行代码的时候，就会进去一个执行上下文。执行上下文可以理解为当前代码的执行环境。他会形成一个作用域。js中运行的环境大概包括三种情况：</p>
      <ul class="infoList">
        <li>全局环境：js代码运行起来会首先进入该环境。</li>
        <li>函数环境：当函数被调用执行时，会进去当前函数中执行代码。</li>
        <li>eval</li>
      </ul>
      <p class="detialInfo">在js程序中，必定会产生多个执行上下文，js引擎会以堆栈的方式来处理它们，这个堆栈，称之为函数调用栈（call stack),栈低永远都是全局上下文，而栈顶就是当前正在执行的上下文。</p>
      <p class="detialInfo">当代码在执行过程中，遇到以上三种情况，都会生成一个执行上下文，放入栈中，而处于栈顶的上下文执行完毕之后，就会自动出栈。如下：</p>
      <pre><code class="javascript">var color = "blue";<br>function changeColor(){<br>&nbsp;var anotherColor = "red";<br> function swapColors(){<br>&nbsp;&nbsp;var tempColor = anotherColor;<br>&nbsp;&nbsp;anotherColor = color;<br>&nbsp;&nbsp;color = tempColor;<br>&nbsp;}<br>&nbsp;swapColors();<br>}<br>changeColor();<br>console.log(color);/*red*/</code></pre>
      <p class="detialInfo">全局上下文在浏览器窗口关闭后出栈。函数中遇到return能直接终止可执行代码的执行，因此会直接将当前上下文弹出栈。详细了解这个过程之后，可以对执行上下文总结一些结论：</p>
      <ul class="infoList">
        <li>单线程</li>
        <li>同步执行，只有栈顶的上下文处于执行中，其他上下文需要等待。</li>
        <li>全局上下文只有唯一的一个，它在浏览器关闭时出栈。</li>
        <li>函数的执行上下文的个数没有限制</li>
        <li>每次某个函数被调用，就会有个新的执行上下文为其创建，即使是调用的自身函数也是如此。</li>
      </ul>
      <p class="detialInfo">下面是一个简单的闭包例子：</p>
      <pre><code class="javascript">function f1(){<br>&nbsp;var n = 999;<br> function f2(){alert(n);}<br>&nbsp;return f2;<br>}<br>var result = f1();<br>result();/*999*/</code></pre>
      <p class="detialInfo">因为f1中的函数f2在f1的可执行代码中，并没有被调用执行，因此执行f1时，f2不会创建新的上下文，而是知道result执行时，才创建了一个新的。</p>
    </div>
    <div id="content6" class="content">
      <h2 class="title">变量对象</h2>
      <p class="detialInfo">变量对象的创建，一次经历了一下几个过程：</p>
      <ul class="infoList">
        <li>建立arguments对象，检查当前上下文中的参数，建立该对象下的属性与属性值。</li>
        <li>检查当前上下文的函数声明，也就是使用function 关键字声明的函数。在变量对象中以函数名建立一个属性，属性值为指向该函数所在内存地址的引用，如果函数名的属性已经存在，那么该属性将会被新的引用所覆盖。</li>
        <li>检查当前上下文的变量声明，每找到一个变量声明，就在变量对象中以变量名建立一个属性，属性值为undefined。如果该变量名的属性已经存在，为了防止同名的函数被修改为undefined，则会直接跳过。，原属性值不会被修改。</li>
      </ul>
      <p class="detialInfo">在上面的规则中看出，function声明会比var声明优先级更高一点。为了更好的理解变量对象，结合以下的例子：</p>
      <pre><code class="javascript">function test(){<br>&nbsp;console.log(a);/*undefined*/;<br>&nbsp;console.log(foo());/*2*/<br>&nbsp;var a = 1;<br>&nbsp;function foo(){return 2;}<br>}<br>test();</code></pre>
      <p class="detialInfo">未进入执行阶段之前，变量对象中的属性都不能访问，但是进去执行阶段之后，变量对象转变为了活动对象，里面的属性都能被访问了。上面的例子执行顺序就变成了这样：</p>
      <pre><code class="javascript">function test(){<br>&nbsp;function foo(){return 2;}<br>&nbsp;var a;<br>&nbsp;console.log(a);/*undefined*/<br>&nbsp;console.log(foo());/*2*/<br>&nbsp;a = 1;<br>}<br>test();</code></pre>
      <p class="detialInfo">再来一个例子，巩固一下我们的理解：</p>
      <pre><code class="javascript">function test(){<br>&nbsp;console.log(foo);/*结果是foo的函数体*/<br>&nbsp;console.log(bar);/*undefined*/<br>&nbsp;var foo = "Hello";/*var 声明的变量当遇到同名的属性时，会跳过而不会覆盖。*/<br>&nbsp;console.log(foo);/*Hello*/<br>&nbsp;var bar = function(){return "word";}<br>&nbsp;function foo(){return "hello";}<br>}<br>test();</code></pre>
       <p class="detialInfo">最后上面的例子的执行顺序就变成了下面这样：</p>
       <pre><code class="javascript">function test(){<br>&nbsp;function foo(){return "hello";}<br>&nbsp;var bar;<br>&nbsp;console.log(foo);<br>&nbsp;console.log(bar);<br>&nbsp;foo = "Hello";<br>&nbsp;console.log(foo);<br>&nbsp;bar = function(){return "word";}<br>}<br>test();</code></pre>
    </div>
    <div id="content7" class="content">
      <h2 class="title">作用域链与闭包</h2>
      <h2 class="subtitle">作用域</h2>
      <ul class="infoList">
        <li>在js中可以将作用域定义为一套规则，这套规则用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量查找。（这里的标识符是变量名或者是函数名）</li>
        <li>js中只有全局作用域和函数作用域。</li>
        <li>作用域与执行上下文是完全不同的两个概念。（js代码的整个执行过程，分为两个阶段，代码编译阶段与代码执行阶段，编译阶段由编译器完成，将代码翻译成可执行代码，这个阶段作用域规则会确定。执行阶段由引擎完成，主要任务是执行可执行代码，执行上下文在这个阶段创建。）</li>
      </ul>
      <h2 class="subtitle">作用域链</h2>
      <p class="detialInfo">作用域是在编译阶段确定规则，作用域链是在执行上下文的创建阶段生成的，作用域是一套规则，作用域链是这套规则的具体实现。</p>
      <p class="detialInfo">作用域链是由当前环境与上层环境的一系列变量对象组成，它保证了当前执行环境对符合访问权限的变量和函数的有序访问。结合下面的例子来理解作用域链：</p>
      <pre><code class="javascript">var a = 20;<br>function test(){<br>&nbsp;var b = a + 10;<br>&nbsp;function innerTest(){<br>&nbsp;&nbsp;var c = 10;return b + c;<br>&nbsp;}<br>&nbsp;return innerTest();<br>}<br>test();/*40*/</code></pre>
      <p class="detialInfo">作用域链是以最前端为起点，最末端为终点的单方向通道更为贴切，所有的最末端都为全局变量对象。作用域链是由一系列变量对象组成，可以在这个单向通道中，查询变量对象中的标识符，这样就可以访问到上一层作用域中的变量了。</p>
      <h2 class="subtitle">闭包</h2>
      <p class="detialInfo">闭包是在函数执行过程中被确认。闭包的定义——当函数可以记住并访问所在的作用域（全局作用域除外）时，就产生了闭包，即使函数是在当前作用域之外执行。</p>
      <p class="detialInfo">js拥有自动的垃圾回收机制，当一个值在内存中失去引用时，垃圾回收机制会根据特殊的算法找到它，并将其回收释放内存。函数的执行上下文，在执行完毕之后，生命周期结束，那么该函数的执行上下文就会失去引用。其占用的内存空间很快就会被垃圾回收器释放。可是闭包的存在会阻止这一过程。来个例子：</p>
      <pre><code class="javascript">var fn = null;<br>function foo(){<br>&nbsp;var a = 2;<br>&nbsp;function innerFoo(){console.log(a);}<br>&nbsp;fn = innerFoo;/*将innerFoo的引用，赋值给全局变量中的fn*/<br>}<br>function bar(){fn();}/*保留的innerFoo的引用*/<br>foo();<br>bar();/*2*/</code></pre>
      <p class="detialInfo">上面例子中，foo()执行完毕之后，按照常理，其执行环境生命周期会结束，所占内存被垃圾收集器释放。但是通过fn = innerFoo，函数innerFoo的引用被保留下来，复制给了全局变量fn，这个行为，导致了foo的变量对象也被保留了下来。函数fn在函数bar内部执行时，依然可以访问这个被保留下来的变量对象。所以此刻仍然能够访问到变量a的值。这样可以称foo为闭包。所以通过闭包，可以在其他的执行上下文中，访问到函数的内部变量。</p>
      <pre><code class="javascript">var fn = null;<br>function foo(){<br>&nbsp;var a = 2;<br>&nbsp;function innerFoo(){console.log(a);<br>&nbsp;&nbsp;console.log(c);}/*在这里试图访问函数bar 中的c变量，会抛出错误*/}<br>&nbsp;fn = innerFoo;<br>}<br>function bar(){var c = 100;fn();}<br>foo();<br>bar();</code></pre>
      <h2 class="subtitle">闭包的应用场景</h2>
      <p class="detialInfo">1、延迟函数setTimeout。看下面的例子：</p>
      <pre><code class="javascript">function fn(){console.log('this is test.');}<br>var timer = setTimeout(fn,1000);<br>console.log(timer);/*先输出timer的值过1s后输出this is test.*/<br><br>function fn(){console.log('this is test.');}<br>var timer = setTimeout(fn(),1000);<br>console.log(timer);/*先输出this is test紧接着输出timer的值*/</code></pre>
      <p class="detialInfo">执行上面的代码。变量timer的值会立即输出，表示setTimeout这个函数本身已经执行完毕，但是一秒钟之后，fn才会被执行。</p>
      <p class="detialInfo">2、柯里化</p>
      <p class="detialInfo">3、模块</p>
      <pre><code class="javascript">(function (){<br>&nbsp;var a = 10, b = 20;<br>&nbsp;function add(num1,num2){<br>&nbsp;&nbsp;var num1 = !!num1 ? num1 : a;<br>&nbsp;&nbsp;var num2 = !!num2 ? num2 : b;/*在undefined和null时，用一个！返回的都是true，<br>用两个！返回的都是false，所以两个！的作用是<br>——如果明确设置了变量的值（非null、undefined、0、""等值）<br>结果就会根据变量的实际值来返回，如果没有设置，结果就会返回false。*/<br>&nbsp;&nbsp;return num1 + num2;}<br>&nbsp;window.add = add;<br>})();<br>add(10,20);/*30*/</code></pre>
      <p class="detialInfo">上面的例子中，使用了函数自执行的方式，创建了一个模块。add是模块对外暴露的一个公共方法，而变量a，b被作为私有变量。在面向对象开发中，常常需要考虑是将变量作为私有变量，还是放在构造函数中的this中，所以原型链和闭包是非常重要的。</p>
      <h2 class="subtitle">setTimeout()</h2>
      <p class="detialInfo">修改下面的代码，让循环输出的结果一次为1,2,3,4,5：</p>
      <pre><code class="javascript">for (var i = 1; i <=5; i++){<br>&nbsp;setTimeout(function timer(){console.log(i);<br>&nbsp;},i*1000);}/*现在都是输出6*/</code></pre>
      <p class="detialInfo">下面是修改后使用到闭包知识：</p>
      <pre><code class="javascript">for (var i = 1; i <=5; i++){<br>&nbsp;setTimeout(function(i){<br>&nbsp;&nbsp;return function(){console.log(i);}<br>&nbsp;}(i),i*1000);}<br><br>for (var i = 1; i <=5; i++){<br>&nbsp;(function(i){setTimeout(function timer(i){<br>&nbsp;console.log(i);},i*1000)})(i);}<br><br>for (var i = 1; i <=5; i++){<br>&nbsp;setTimeout(function timer(){<br>&nbsp;console.log(i);},i*1000,i);}</code></pre>
      <p class="detialInfo">函数执行前，函数内部变量均被声明</p>
      <pre><code class="javascript">(function(){<br>&nbsp;console.log(a);/*undefined,不报错*/<br>&nbsp;if(false){<br>&nbsp;&nbsp;var a = 1;/*不会执行到，但是被声明*/<br>}})()</code></pre>
      <p class="detialInfo">异步加载js减少阻塞，js不要频繁读写Dom减少reflow/paint</p>
    </div>
    <div id="content8" class="content">
      <h2 class="title">应该知道的setTimeout秘密</h2>
      <h2 class="subtitle">setTimeout原理</h2>
      <pre><code class="javascript">var start = new Date(),end = 0;<br>setTimeout(function(){<br>&nbsp;console.log(new Date() - start);/*肯定是大于1000的*/<br>},500);<br>while(new Date() - start <= 1000){}</code></pre>
      <p class="detialInfo">上面的代码为什么不是500？那是因为js是单线程执行的，只有一个线程在运行js程序。无法同时运行多段代码。再看看浏览器下的js:浏览器的内核是多线程的，他们在内核控制下相互配合以保持同步，一个浏览器至少实现三个常驻线程。如下</p>
      <ul class="infoList">
        <li>js引擎——是基于事件驱动单线程执行的，js引擎一直等待着任务队列中任务的到来，然后加以处理，浏览器无论什么时候都只有一个js线程在运行js程序。</li>
        <li>GUI渲染线程——负责渲染浏览器界面，当界面需要重绘（Repaint）或由于某种操作引发回流（Reflow）时，该线程就会执行。需要注意的是，GUI渲染线程与js引擎是互斥的，当js引擎执行时，GUI线程会被挂起，GUI更新会被保存在一个队列中等到js引擎空闲时立即被执行。</li>
        <li>事件触发线程——当一个事件被触发时，该线程会把事件添加到待处理列队的队尾，等待js引擎的处理。这些事件可来自js引擎当前执行的代码块如setTimeout、也可来自浏览器内核的其他线程如鼠标点击、ajax异步请求等，但由于js的单线程关系，所有这些事件都得排队等待js引擎处理。（当线程中没有执行任何同步代码的前提下才会执行异步代码）</li>
      </ul>
      <p class="detialInfo">再看下上面的例子:虽然setTimeout的延迟时间是500ms，由于while循环的存在，只有当时间间隔大于1000ms时才会跳循环，就相当于在1000ms之前，while循环一直在占用着js线程，只有等跳出循环后，线程才会空闲下来，才会去执行之前定义的setTimeout。setTimeout只能保证在指定的时间后将任务插入任务列队中等候，但是不保证这个任务在什么时候执行，一旦执行js的线程空闲出来，自行从队列中取出任务然后执行它。</p>
      <h2 class="subtitle">setTimeout的好搭档0</h2>
      <pre><code class="javascript">setTimeou(function(){/*some code*/},0);</code></pre>
      <p class="detialInfo">上面的代码表示立即执行，本意是立刻执行调用函数，但事实上面的带按摩并不是立即执行的，是因为setTimeout有一个最小执行时间，当指定的时间小于该时间时，浏览器会用最小允许的时间作为setTimeout的时间间隔，所以即使把setTimeout的延迟时间设置为0被调用的程序也没有马上启动。</p>
      <p class="detialInfo">设置setTimeout(fn,0)的时候，实际是实现插队操作，要求浏览器“尽可能”的进行回调，但是实际能有多快就完全取决于浏览器了。setTimeout(fn,0)的实际用处是——可以改变执行的顺序，因为浏览器会在执行完当前队列中的任务，再执行setTimeout队列中积累的任务。通过设置任务在延迟到0s后执行，就能改变任务执行的先后顺序，延迟任务发生，使之异步执行。如下面网上很流行的例子：</p>
      <pre><code class="javascript">document.querySelector("#test input").onkeydown = function(){<br>&nbsp;document.querySelector("#test span").innerHTML = this.value;<br>};<br>document.querySelector("#test input").onkeydown = function(){<br>&nbsp;setTimeout(function(){<br>&nbsp;&nbsp;document.querySelector("#test span").innerHTML = <br>&nbsp;&nbsp;document.querySelector("#test input").value;<br>&nbsp;},0);<br>};</code></pre>
      <p class="detialInfo">当你往表单输入内容时，会发现未使用setTimeout函数的只会获取到输入的前的内容，使用setTimeout函数的则会获取到输入的内容。因为，当按下按键时，js引擎需要执行keydown的事件处理程序，然后更新文本框的value值，这两个任务也需要按顺序来，事件处理程序执行时，更新value（是在keypress后）的任务则进入队列等待，所以我们在keydown的事件处理程序里是无法得到更新后的value值。使用setTimeout(fn,0)把取value值的操作放入队列，放在更新value值以后，这样可以获取出文本框的值。</p>
      <ul class="infoList">
        <li>未使用setTimeout函数，执行顺序是：onkeydown->onkeypress->onkeyup</li>
        <li>使用setTimeout函数，执行顺序是：onkeydown->onkeypress->function->onkeyup</li>
      </ul>
      <p class="detialInfo">虽然可以用keyup来代替keydown，但长按时，keyup并不会触发。长按时，keydown、keypress、keyup的调用顺序是：keydown->keypress->keydown->keypress->...->keyup。这说明长按时keyup只会触发一次，无法用keyup来实时获取值。</p>
      <h2 class="subtitle">setTimeout中回调函数的this</h2>
      <p class="detialInfo">setTimeout()方法是浏览器window对象提供的，因此第一个参数函数中this其实是指向window对象，如下例子：</p>
      <pre><code class="javascript">var a = 1;<br>var obj = {<br>&nbsp;a:2,<br>&nbsp;test:function(){<br>&nbsp;&nbsp;setTimeout(function(){<br>&nbsp;&nbsp;&nbsp;console.log(this.a);},0);}};<br>obj.test();/*1*/</code></pre>
      <p class="detialInfo">可以通过使用bind()方法来改变setTimeout回调函数里的this：</p>
      <pre><code class="javascript">var a = 1;<br>var obj = {<br>&nbsp;a:2,<br>&nbsp;test:function(){<br>&nbsp;&nbsp;setTimeout(function(){<br>&nbsp;&nbsp;&nbsp;console.log(this.a);}.bind(this),0);}};<br>obj.test();/*2*/</code></pre>
      <h2 class="subtitle">setTimeout不止两个参数</h2>
      <p class="detialInfo">还可以传入第三个，第四个...，他们是用来表示第一个参数（回调函数）传入的参数，如下：</p>
      <pre><code class="javascript">setTimeout(function(a,b){<br>&nbsp;console.log(a);/*3*/<br>&nbsp;console.log(b)/*4*/<br>},0,3,4);</code></pre>
    </div>
    <div id="content9" class="content">
      <h2 class="title">js中的call、apply、bind方法</h2>
      <p class="detialInfo">在js中call、apply、bind是function对象自带的三个方法，这三个方法的主要作用是改变函数中的this指向，三者第一参数都是this要指向的对象，也就是想指定的上下文。三者都可以利用后续参数传参。bind是返回对应函数，便于稍后调用，apply、call则是立即调用。</p>
      <h2 class="subtitle">call()</h2>
      <ul class="infoList">
        <li>调用一个对象的一个方法，以另一个对象替换当前对象。</li>
        <li>call方法可以用来代替另一个对象调用一个方法。</li>
        <li>call方法可将一个函数的对象上下文从初始的上下文改变为由thisObj指定的新对象。</li>
      </ul>
      <p class="detialInfo">thisObj的取值有以下4种情况：</p>
      <ul class="infoList">
        <li>不传或者传null、undefined，函数中的this指向window对象。</li>
        <li>传递另一个函数的函数名，函数中的this指向这个函数的引用。</li>
        <li>传递字符串、数值或布尔类型等基础类型，函数中的this指向其对应的包装对象，如String、Number、Boolean。</li>
        <li>传递一个对象，函数中的this指向这个对象。</li>
      </ul>
      <pre><code class="javascript">function a(){console.log(this);}<br>function b(){}<br>var c = {name:"call"};<br>a.call();/*window*/<br>a.call(null);/*window*/<br>a.call(undefined);/*window*/<br>a.call(1);/*Number*/<br>a.call('');/*String*/<br>a.call(true);/*boolean*/<br>a.call(b)/*function b(){}*/<br>a.call(c);/*Object*/<br><br>function class1(){<br>&nbsp;this.name = function(){console.log("class1 function");<br>&nbsp;}<br>}<br>function class2(){<br>&nbsp;class1.call(this);/*当前的this指向了class1（也可以说class2继承了class1）*/<br>}<br>var f = new class2();<br>f.name();/*调用class1内的方法，将class1的name方法交给class2使用*/</code></pre>
      <p class="detialInfo">常用例子如下：</p>
      <pre><code class="javascript">function eat(x,y){console.log(x +y);}<br>function drink(x,y){console.log(x-y);}<br>eat.call(drink,3,2);/*5*/</code></pre>
      <p class="detialInfo">上面的例子是用eat来替换drink，eat.call(drink,3,2) == eat(3,2)。js中函数其实是对象，函数名是对Function对象的引用。</p>
      <pre><code class="javascript">function Animal(){<br>&nbsp;this.name = "animal";<br>&nbsp;this.showName = function(){<br>&nbsp;&nbsp;console.log(this.name);<br>&nbsp;}<br>}<br>function Dog(){this.name = "dog";}<br>var animal = new Animal();<br>var dog = new Dog();<br>animal.showName.call(dog);/*dog*/</code></pre>
      <p class="detialInfo">animal.showName.call(dog)——把animal的方法放到dog上执行或者把animal的showName()方法放到dog上来执行。</p>
      <pre><code class="javascript">function Animal(name){<br>&nbsp;this.name = name;<br>&nbsp;this.showName = function(){<br>&nbsp;&nbsp;console.log(this.name);<br>&nbsp;}<br>}<br>function Dog(name){Animal.call(this,name);}<br>var dog = new Dog("Crazy dog");<br>dog.showName();/*Crazy dog*/</code></pre>
      <p class="detialInfo">Animal.call(this)的意思就是使用Animal对象代替this对象，那么Dog就能直接调用Animal的所有属性和方法（继承）。</p>
      <h2 class="subtitle">apply()</h2>
      <p class="detialInfo">应用某一对象的一个方法，用另一个对象替换当前对象。如果argArray不是一个有效的数组或者不是arguments对象，那么将导致一个TypeError。如果没有提供argArray和thisObj任何一个参数，那么Global对象将被用作thisObj，并且无法传递任何参数。</p>
      <p class="detialInfo">call和apply的区别——作用完全一样，只是接受参数的方式不太一样：</p>
      <pre><code class="javascript">function class1(args1,args2){<br>&nbsp;this.name = function(){<br>&nbsp;&nbsp;console.log(args1,args2);<br>&nbsp;}<br>}<br>function class2(){<br>&nbsp;var args1 = "1",args2 = "2;"<br>&nbsp;class1.call(this,args1,args2);<br>&nbsp;/*或者class1.apply(this,[args1,args2]);*/<br>}<br>var c = new class2();<br>c.name();/*1 2*/</code></pre>
      <p class="detialInfo">call需要把参数按顺序传递进去，apply是把参数放在数组里。当参数是明确知道数量时用call，不确定是用apply，然后把参数push进数组传递进去。当参数数量不确定时，函数内部也可以通过arguments这个数组来遍历所有的参数。</p>
      <h2 class="subtitle">bind</h2>
      <p class="detialInfo">bind()方法会穿件一个新函数，称为绑定函数，当调用这个绑定函数时，绑定函数会以创建它时传入bind()方法的第一个参数作为this，传入bind()方法的第二个以及以后的参数加上绑定函数运行时本身的参数按照顺序作为原函数的参数来调用原函数。</p>
      <pre><code class="javascript">var bar = function(){<br>&nbsp;console.log(this.x);<br>}<br>var foo = {x:3};<br>bar()/*undefined*/<br>bar.bind(foo)();/*3*/<br>/*或 var func = bar.bind(foo);func();*/</code></pre>
    </div>
  </div>
  <div class="column sideCol l">
    <ul class="title-list">
      <li><a class="link" href="#content1">栈与堆</a></li>
      <li><a class="link" href="#content2">变量对象与基础数据类型</a></li>
      <li><a class="link" href="#content3">引用数据类型与堆内存</a></li>
      <li><a class="link" href="#content4">内存空间管理</a></li>
      <li><a class="link" href="#content5">执行上下文</a></li>
      <li><a class="link" href="#content6">变量对象</a></li>
      <li><a class="link" href="#content7">作用域链与闭包</a></li>
      <li><a class="link" href="#content8">应该知道的setTimeout秘密</a></li>
      <li><a class="link" href="#content9">js中的call、apply、bind方法</a></li>
    </ul>
  </div>
</div>